package cn.edu.buaa.treehole.service;

import cn.edu.buaa.treehole.config.Setting;
import cn.edu.buaa.treehole.dao.PostDao;
import cn.edu.buaa.treehole.dao.exception.DaoException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class PostListService {
    private static final int cacheSize = 128;
    private static final int capacityTimes = 2;


    private final PostDao postDao;
    private ReplyListService replyListService;
    private HotListService hotListService;
    private Setting setting;

    @Autowired
    public PostListService(PostDao postDao) {
        this.postDao = postDao;
    }

    @Autowired
    public void setReplyListService(ReplyListService replyListService) {
        this.replyListService = replyListService;
    }

    @Autowired
    public void setHotListService(HotListService hotListService) {
        this.hotListService = hotListService;
    }

    public Setting getSetting() {
        return setting;
    }

    @Autowired
    public void setSetting(Setting setting) {
        this.setting = setting;
    }

    private int beg = 0;
    private int end = 0;
    private final long[] cache = new long[cacheSize * capacityTimes];
    private final HashSet<Long> cachedPid = new HashSet<>();


    public void deletePostCache(long pid) {
        if (setting.blockListCache()) {
            return;
        }
        cachedPid.remove(pid);
        replyListService.deleteAllRepliesForPost(pid);
        if (end > beg) {
            if (pid == cache[end - 1]) {
                end--;
            }
            else if (pid == cache[beg]) {
                beg++;
            }
        }
    }

    public long getLatest() throws DaoException {
        if (setting.blockListCache()) {
            return postDao.getLatestPostPid();
        }
        if (end == beg) {
            refillFromDatabase();
        }
        return cache[end - 1];
    }

    public long getEarliest() throws DaoException {
        for (int index = 0; true; index++) {
            if (postDao.contain(index)) {
                return index;
            }
        }
    }

    public boolean isCachedPost(long pid) {
        return !setting.blockListCache() && cachedPid.contains(pid);
    }

    public boolean contain(long pid) throws DaoException {
        if (!setting.blockListCache()) {
            if (end == beg) {
                refillFromDatabase();
            }
            if (cachedPid.contains(pid)) {
                return true;
            }
            if (pid > cache[beg]) {
                return false;
            }
        }
        return postDao.contain(pid);
    }

    public void addPostCache(long pid) throws DaoException {
        if (setting.blockListCache()) {
            return;
        }
        if (end == cache.length) {
            allocCache();
        }
        replyListService.cacheAllRepliesForPostFromDatabase(pid);
        cache[end++] = pid;
        cachedPid.add(pid);
    }

    private void refillFromDatabase() throws DaoException {
        end = cacheSize;
        beg = end;
        long latest = postDao.getLatestPostPid();
        cache[--beg] = latest--;
        while (beg > 0 && latest >=0) {
            if (postDao.contain(latest)) {
                replyListService.cacheAllRepliesForPostFromDatabase(latest);
                cache[--beg] = latest--;
            }
        }
    }

    /*
    * 保证cache中还有cacheSize空间
    * */
    private void allocCache() {
        while (beg < cacheSize) {
            if (beg == end) {
                end = 0;
                return;
            }
            if (!hotListService.contain(cache[beg])) {
                replyListService.deleteAllRepliesForPost(cache[beg]);
            }
            cachedPid.remove(cache[beg++]);
        }
        int dst = 0;
        while (beg < end) {
            cache[dst++] = cache[beg++];
        }
        end = dst;
    }
}
